들어가며
안드로이드 개발을 하다 보면 ADB 명령어를 반복적으로 사용하게 됩니다. 특히 여러 디바이스에 APK를 설치하거나, 앱 정보를 확인하는 작업은 매번 긴 명령어를 입력해야 해서 번거로웠습니다.
그래서 개인적으로 사용할 목적으로 간단한 쉘 스크립트 두 개를 만들어 보았습니다. 혹시 비슷한 불편함을 겪고 계신 분들께 도움이 될 수도 있을 것 같아 소개해드립니다.
결과물 소개
다운로드: https://github.com/Claude-H/adb-extensions
두 개의 스크립트로 구성되어 있습니다:
만들게 된 계기
평소 개발하면서 이런 상황들이 자주 있었습니다:
- 여러 디바이스에 같은 APK 설치할 때마다
adb devices
→ 디바이스 ID 복사 →adb -s DEVICE_ID install app.apk
반복 - 포그라운드 앱의 패키지명을 몰라서
adb shell dumpsys activity
명령어로 찾아야 하는 번거로움 - APK 설치 실패 시
-t
,-r
,-d
같은 옵션들을 하나씩 시도해보기 - 앱 정보나 권한을 확인하려면 복잡한 dumpsys 명령어 조합 필요
이런 반복 작업들이 귀찮아서 간단한 래퍼 스크립트를 만들어 보기로 했습니다.
설치 및 환경 설정
1. 사전 준비사항
필수 요구사항:
- Android SDK의
adb
도구 (PATH에 등록되어 있어야 함) - Bash 쉘 환경 (macOS/Linux)
선택 요구사항:
- 서명 해시 추출 기능 사용 시:
ANDROID_HOME
환경 변수 설정 필요
2. ANDROID_HOME 설정
서명 분석 기능(ak signature
)을 사용하려면 환경 변수 설정이 필요합니다:
# ~/.bashrc 또는 ~/.zshrc에 추가
export ANDROID_HOME="/Users/your-username/Library/Android/sdk"
3. 스크립트 설치
# 저장소 클론
git clone https://github.com/Claude-H/adb-extensions.git
cd adb-extensions
# 자동 설치 (권장)
sudo ./ai.sh --install
sudo ./ak.sh --install
자동 설치 시 다음 작업이 수행됩니다:
/usr/local/bin
에 스크립트 복사 (ai
,ak
명령어로 사용 가능)- 실행 권한 부여
- macOS 격리 속성 제거 (
xattr -d com.apple.quarantine
)
ai.sh - APK Installer
명령어 사용법
ai [옵션] [apk파일...]
사용 가능한 옵션
기본 옵션
옵션 | 설명 |
---|---|
-v, --version |
스크립트 버전 출력 |
-h, --help |
도움말 출력 |
APK 선택 옵션
옵션 | 설명 |
---|---|
-l |
현재 디렉토리에서 가장 최신 APK 설치 |
-a |
현재 디렉토리의 모든 APK 설치 |
-s |
설치할 APK를 사용자에게 선택하도록 인터랙티브 제공 |
디바이스 타겟팅 옵션
옵션 | 설명 |
---|---|
-m |
연결된 모든 디바이스에 APK 설치 |
ADB 설치 옵션
옵션 | 설명 |
---|---|
-r |
기존 앱 덮어쓰기 설치 (adb install -r, 기본값) |
-t |
테스트 APK 설치 허용 |
-d |
버전 코드 다운그레이드 허용 (패키지 매니저 권한 필요) |
주요 기능
가장 자주 쓰는 APK 설치 패턴들을 간단한 옵션으로 만들었습니다:
1. 간편한 APK 선택
스크립트는 세 가지 APK 선택 모드를 지원합니다:
# 최신 APK 자동 선택
ai -l
# 모든 APK 배치 설치
ai -a
# 인터랙티브 선택
ai -s
동작 흐름
ai.sh는 사용자가 선택한 옵션에 따라 다음과 같은 지능형 설치 프로세스를 수행합니다:
핵심 구현 특징:
- .idsig 파일 자동 감지: APK와 함께
.idsig
파일이 존재할 경우--no-incremental
옵션을 자동으로 적용하여 인크리멘탈 설치 문제를 방지 - 다단계 복구 메커니즘: 설치 실패 시 발생한 에러 유형에 따라
-t
,-d
옵션 적용 또는 기존 앱 제거 후 재설치 등, 상황에 맞는 복구 절차를 자동으로 수행함 - 다중 디바이스 일괄 설치:
-m
옵션으로 연결된 모든 디바이스에 일괄 설치 지원
2. 다중 디바이스 관리
복수의 안드로이드 디바이스가 연결된 환경에서 효율적으로 작업할 수 있도록 설계되었습니다:
# 연결된 모든 디바이스에 설치
ai -m app.apk
# 디바이스 선택 UI
present_device_selection() {
echo -e "${BARROW} ${BOLD}List of connected devices: $device_count${NC}"
local i=1
for device_info in "${device_list[@]}"; do
echo -e "[${BOLD}$i${NC}] ${YELLOW}$(pretty_device $device_info)${NC}"
((i++))
done
}
3. 지능형 설치 실패 복구
설치 실패 시나리오에 대한 자동 복구 메커니즘이 구현되어 있습니다:
- 기본 설치 시도 (
adb install -r
) - 테스트 APK 모드 (
-t
옵션 추가) - 다운그레이드 허용 (
-d
옵션 추가) - 앱 데이터 보전 경고와 함께 재설치 안내
execute_install_command() {
case "$result" in
*INSTALL_FAILED_TEST_ONLY*)
retry_install "INSTALL_FAILED_TEST_ONLY" "-t" "${device_opt}" "${install_opt}" "${apk_file}"
;;
*INSTALL_FAILED_VERSION_DOWNGRADE*)
if [[ "$install_opt" == *"-d"* ]]; then
resolve_downgrade "${device_opt}" "${install_opt}" "${apk_file}"
else
retry_install "INSTALL_FAILED_VERSION_DOWNGRADE" "-d" "${device_opt}" "${install_opt}" "${apk_file}"
fi
;;
*INSTALL_FAILED_UPDATE_INCOMPATIBLE*)
resolve_conflict "${device_opt}" "${install_opt}" "${apk_file}" "${result}"
;;
esac
}
ak.sh - APK 관리 도구
명령어 사용법
ak <명령어> [패키지명] [추가 인자...]
사용 가능한 옵션
기본 옵션
옵션 | 설명 |
---|---|
-v, --version |
스크립트 버전 출력 |
-h, --help |
도움말 출력 |
사용 가능한 명령어
명령어 | 사용 예시 | 설명 |
---|---|---|
pull |
ak pull [packageName] [outputFile] |
지정한 앱의 APK 파일을 로컬로 저장합니다. 출력 파일명을 생략하면 [packageName].apk로 저장됩니다. |
info |
ak info [packageName] |
앱의 버전, 설치 시점, 데이터 경로 등 핵심 정보를 조회합니다. |
permissions |
ak permissions [packageName] |
앱이 요청한 권한 목록을 출력합니다. |
uninstall |
ak uninstall [packageName] |
앱을 디바이스에서 제거합니다. |
kill |
ak kill <packageName1> [packageName2 ...] |
하나 이상의 앱 프로세스를 강제 종료합니다. 첫 번째 패키지명은 필수이며, 이후는 선택적으로 추가할 수 있습니다. |
devices |
ak devices |
연결된 디바이스 목록과 상태 정보를 출력합니다. |
launch |
ak launch <packageName> |
앱의 런처 액티비티를 실행합니다. |
signature |
ak signature [packageName] |
앱의 SHA-256 서명 해시를 출력합니다. ANDROID_HOME 환경 변수가 설정되어 있어야 합니다. |
참고: [packageName]
을 생략하면 현재 포그라운드 앱 기준으로 동작합니다.
주요 기능
APK 설치 외에 필요한 관리 작업들을 모아놨습니다:
1. 현재 앱 자동 감지
현재 포그라운드 애플리케이션을 자동으로 감지하는 기능:
detect_foreground_package() {
adb -s "$G_SELECTED_DEVICE" shell dumpsys activity activities | \
grep -i Hist | head -n 1 | \
sed -n 's/.* u0 \([^\/ ]*\)\/.*/\1/p'
}
2. 패키지 유효성 검사
validate_package_or_exit() {
local package_name="$1"
if ! validate_package_name "$package_name"; then
echo -e "${RED}✘ Invalid package name format:${NC} $package_name"
exit 1
fi
if ! is_package_installed "$package_name"; then
echo -e "${RED}✘ Package not installed on the device:${NC} $package_name"
exit 1
fi
}
3. APK 서명 분석 기능
apksigner
를 활용한 SHA-256 서명 해시 추출 (ANDROID_HOME 설정 필요):
get_signature_info() {
# APK 임시 추출
adb -s "$G_SELECTED_DEVICE" pull "$apk_path" "$tmp_apk" > /dev/null
# apksigner로 서명 정보 추출
apksigner=$(find "$ANDROID_HOME/build-tools" -name apksigner | sort -V | tail -n 1)
signature_output=$("$apksigner" verify --print-certs "$tmp_apk" 2>&1)
# 결과 파싱 및 출력
echo "$signature_output" | grep -v '^WARNING:' | while IFS= read -r line; do
if echo "$line" | grep -q 'SHA-256'; then
echo -e "${GREEN}${BOLD}${line}${NC}"
else
echo "$line"
fi
done
}
구현하면서 신경 쓴 부분들
1. 가독성 있는 출력
터미널에서 한눈에 알아볼 수 있도록 색상으로 구분했습니다:
RED='\033[1;31m' # 에러
GREEN='\033[1;32m' # 성공
YELLOW='\033[1;33m' # 경고/정보
BLUE='\033[1;34m' # 일반 정보
CYAN='\033[1;36m' # 강조
2. macOS 환경 고려
개발 환경이 macOS라서 특별히 신경 쓴 부분들이 있습니다:
install_script() {
local src_path="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
local filename="${filename%.sh}" # .sh 확장자 제거
local dest_path="/usr/local/bin/$filename"
cp "$src_path" "$dest_path"
chmod +x "$dest_path"
xattr -d com.apple.quarantine "$dest_path" 2>/dev/null # macOS 격리 해제
}
3. 사용자 실수 방지
실제 사용하다 보니 자주 하는 실수들을 미리 체크하도록 했습니다:
handle_option_combinations() {
if [ $opt_l_used -eq 1 ] && [ $opt_a_used -eq 1 ] && [ $opt_s_used -eq 1 ]; then
echo -e "${ERROR} Options -l, -a, and -s cannot be used together."
exit 1
fi
# ... 추가 검증 로직
}
사용 예시
# 최신 APK를 모든 기기에 설치
ai -l -m
# 포그라운드 앱의 권한 목록 출력
ak permissions
# SHA-256 서명 해시 출력
ak signature com.example.app
간단한 최적화
1. ADB 호출 최소화
디바이스 정보를 한 번에 가져와서 여러 번 파싱하는 방식으로 ADB 호출 횟수를 줄였습니다:
pretty_device() {
local device_id="$1"
local props=$(adb -s "$device_id" shell getprop)
# 한 번의 호출로 모든 속성 정보 획득
brand=$(echo "$props" | awk -F'[][]' '$2 == "ro.product.brand" {print $4}')
model=$(echo "$props" | awk -F'[][]' '$2 == "ro.product.model" {print $4}')
version=$(echo "$props" | awk -F'[][]' '$2 == "ro.build.version.release" {print $4}')
api=$(echo "$props" | awk -F'[][]' '$2 == "ro.build.version.sdk" {print $4}')
}
2. 반복 작업 처리
여러 APK나 디바이스를 처리할 때 중첩 루프로 효율적으로 처리합니다:
for d in "${selected_device[@]}"; do
for apk_file in "${apk_files[@]}"; do
execute_install_command "-s $d" "$install_opt" "$apk_file"
done
done
개발자 경험(DX) 개선 효과
기존 워크플로우 vs 개선된 워크플로우
다중 디바이스 APK 설치 시나리오
기존의 복잡한 ADB 명령어 작업을 단순화하여 개발 생산성을 높인 사례입니다.
기존 ADB 명령 vs. ai 명령: 플로우 차이점 한눈에 보기
보안 고려사항
1. 패키지명 검증
정규식을 통한 엄격한 패키지명 형식 검증:
validate_package_name() {
local pkg="$1"
[[ "$pkg" =~ ^[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+$ ]]
}
2. 사용자 확인 프로세스
중요한 작업(앱 삭제 등) 전 사용자 확인:
echo -n "Do you want to uninstall and reinstall the application [y/n]? "
stty -echo -icanon
choice=$(dd bs=1 count=1 2>/dev/null)
stty echo icanon
마치며
ADB Extensions는 Shell Script의 강력함과 실용성을 입증하는 프로젝트입니다. 단순해 보이는 도구이지만, Android 개발 워크플로우에서 실질적인 생산성 향상을 달성했습니다. 개인 토이 프로젝트로 시작했지만, 실무에서 충분히 활용 가능한 수준의 완성도와 안정성을 갖추고 있습니다.
실제 사용해보니 좋은 점들:
- 복잡한 ADB 명령어 조합을 기억할 필요 없음
- 설치 실패 시 자동으로 다른 옵션 시도
- 여러 디바이스 작업이 편해짐
- 현재 실행 중인 앱 자동 감지로 패키지명 찾는 수고 덜어짐
한계:
- 기본적인 기능만 구현 (복잡한 시나리오는 여전히 직접 ADB 사용 필요)
- macOS 위주로 테스트 (다른 환경에서는 일부 기능 동작 안 할 수도)
이 프로젝트가 Android 개발 커뮤니티에 기여할 수 있는 작은 도구가 되기를 희망하며, 더 많은 개발자들이 반복적인 작업에서 벗어나 창의적인 개발에 집중할 수 있는 환경을 만드는 데 도움이 되었으면 합니다.